"use strict";
/**
 * 应用入口
 * Created by liamjung on 2018/5/21.
 */
require("co")(function* () {

    let config = require("./app_config");

    let appConfig;

    //加载配置
    if (typeof config === "function") {
        appConfig = yield config();
    } else {
        appConfig = global.appConfig = config;
    }

    const {Log} = require("@libfintech/api-gateway-core/log/log");
    global.Log = Log;

    const {ResponseError} = require("@libfintech/api-gateway-core/error/response_error");
    const {Pipeline} = require("@libfintech/api-gateway-core/entity/pipeline");

    const Koa = require("koa");
    const bodyParser = require("koa-bodyparser");
    const logger = require("koa-logger");
    const stripAnsi = require("strip-ansi");
    const fs = require("fs");
    const appRoot = require("app-root-path");

    const app = new Koa();
    app.on("error", (err, ctx) => Log.error("server error", err, ctx));

    /**
     * 响应客户端
     * @param ctx
     * @param resp
     */
    async function responseClient(ctx, resp) {

        //设置状态
        ctx.response.status = resp.status;

        if (resp.headers) {
            //设置响应头
            await ctx.response.set(resp.headers);
        }
        if (!ctx.type || ctx.type === "")
            ctx.type = "application/json";
        ctx.length = !resp.body ? 0 : Buffer.byteLength(resp.body, "utf8");

        //刷新任何已设置的响应头，并开始主体
        ctx.response.flushHeaders();

        //设置响应体
        ctx.response.body = resp.body;
    }


    //中间件
    app.use(bodyParser({
        extendTypes: {
            //will parse text/json type body as a JSON string
            json: ["text/json"]
        }
    }));
    app.use(logger((str, args) => {
        Log.info(stripAnsi(str));
    }));

    //初始化
    app.use(async (ctx, next) => {

        //初始化管道
        ctx.pipeline = new Pipeline(ctx);

        ctx.pipeline.logger.debug("headers:\t\t" + JSON.stringify(ctx.request.headers));
        ctx.pipeline.logger.debug(ctx.request.method + ":" + ctx.pipeline.request.originalPath);
        ctx.pipeline.logger.debug("querystring:\t\t" + ctx.pipeline.request.querystring);
        ctx.pipeline.logger.debug("body:\t\t" + ctx.pipeline.request.body);
        ctx.pipeline.logger.debug("----------------------------------------------------------------------------------------------------------------------");

        await next();
    });

    //引入插件
    appConfig.plugins.forEach((item, index) => {

        let path = item.path;

        if (!item.enable) {
            //未开启时，忽略
            Log.info("plugin (index: " + index + "; path: " + path + ") is not enabled");

            return;
        }

        //入口方法名称必须为main
        let main = require(path + "/index").main;

        let configPath = item.config_path;

        //插件配置
        let configs = {};
        if (configPath) {
            let configFiles = fs.readdirSync(appRoot.path + item.config_path);
            for (let cf of configFiles) {
                if (cf.endsWith(".conf.js"))
                    configs = Object.assign(configs, require("." + item.config_path + "/" + cf));
            }
        }

        app.use(async (ctx, next) => {

            ctx.pipeline.logger.info("plugin (index: " + index + "; path: " + path + ")");

            try {
                await main(ctx.pipeline, configs);

                await next();
            } catch (err) {

                if (err instanceof ResponseError) {
                    //响应客户端
                    await responseClient(ctx, {
                        status: err.status,
                        headers: err.headers,
                        body: err.body
                    });
                } else
                    ctx.pipeline.logger.error("server error", err, ctx)
            }
        });

        Log.info("load plugin (index: " + index + "; path: " + path + ")");
    });

    app.use(async (ctx, next) => await responseClient(ctx, ctx.pipeline.response));

    app.listen(appConfig.port);
});